import _template from "lodash.template";
import _get from "lodash.get";
import _lowerFirst from "lodash.lowerfirst";
import chalk from "chalk";
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { rootPath, configRootPath } from "./path.config";

export interface ConfigInfo {
  /**
   * 组件系列前缀
   */
  series: string;
  /**
   * 组件源码路径
   */
  src: string;
  /**
   * 组件配置
   */
  component: {
    /**
     * 组件的目录
     */
    dir: string;
    /**
     * 组件的模板文件路径
     */
    templateFilePath: string;
    /**
     * 组件的文件名
     */
    fileName: string;
    /**
     * 组件的入口模板
     */
    entryTemplate: string;
    /**
     * 组件的入口路径
     */
    entryPath: string;
  };
  type?: {
    /**
     * 类型的目录
     */
    dir: string;
    /**
     * 类型的模板文件路径
     */
    templateFilePath: string;
    /**
     * 类型的文件名
     */
    fileName: string;
    /**
     * 类型的入口模板
     */
    entryTemplate: string;
    /**
     * 类型的入口路径
     */
    entryPath: string;
  };
  /**
   * 样式配置
   */
  style: {
    /**
     * 样式的目录
     */
    dir: string;
    /**
     * 样式的模板文件路径
     */
    templateFilePath: string;
    /**
     * 样式的文件名
     */
    fileName: string;
    /**
     * 样式的变量模板
     */
    varTemplate: string;
    /**
     * 样式的变量路径
     */
    varPath: string;
    /**
     * 样式的入口模板
     */
    entryTemplate: string;
    /**
     * 样式的入口路径
     */
    entryPath: string;
  };
  /**
   * 组件示例路径
   */
  srcExample: string;
  /**
   * 示例配置
   */
  example: {
    /**
     * 示例的目录
     */
    dir: string;
    /**
     * 示例的模板文件路径
     */
    templateFilePath: string;
    /**
     * 示例的文件名
     */
    fileName: string;
    /**
     * 示例的入口模板
     */
    entryTemplate: string;
    /**
     * 示例的入口路径
     */
    entryPath: string;
  };
}

type KeyJoin<T> = {
  [K in Extract<keyof T, string>]: T[K] extends string | boolean | number
    ? K
    : `${K}.${KeyJoin<T[K]>}`;
}[Extract<keyof T, string>];

export type ConfigInfoKey = KeyJoin<Required<ConfigInfo>>;

/** 解析后的路径信息-基础 */
export interface ResolvedPathsBase {
  /**
   * 组件目录路径
   */
  componentDirPath: string;
  /**
   * 组件文件内容
   */
  componentFileContent: string;
  /**
   * 组件文件路径
   */
  componentFilePath: string;

  /**
   * 组件入口文件追加内容
   */
  componentEntryFileAddContent: string;
  /**
   * 组件入口文件路径
   */
  componentEntryFilePath: string;

  /**
   * 样式目录路径
   */
  styleDirPath: string;
  /**
   * 样式文件内容
   */
  styleFileContent: string;
  /**
   * 样式路径
   */
  styleFilePath: string;

  /**
   * 样式变量文件追加内容
   */
  styleVarFileAddContent: string;
  /**
   * 样式变量路径
   */
  styleVarFilePath: string;

  /**
   * 样式入口文件追加内容
   */
  styleEntryFileAddContent: string;
  /**
   * 样式入口路径
   */
  styleEntryFilePath: string;

  /**
   * 示例目录路径
   */
  exampleDirPath: string;
  /**
   * 示例文件内容
   */
  exampleFileContent: string;
  /**
   * 示例文件路径
   */
  exampleFilePath: string;

  /**
   * 示例入口文件追加内容
   */
  exampleEntryFileAddContent: string;
  /**
   * 示例入口路径
   */
  exampleEntryFilePath: string;
}

/** 模板有单独的type目录 */
export interface ResolvedPathsHaveType extends ResolvedPathsBase {
  /**
   * 使用(单独创建的)type目录
   */
  useTypeDir: true;

  /**
   * 类型目录路径
   */
  typeDirPath: string;
  /**
   * 类型文件内容
   */
  typeFileContent: string;
  /**
   * 类型文件路径
   */
  typeFilePath: string;

  /**
   * 类型入口文件追加内容
   */
  typeEntryFileAddContent: string;
  /**
   * 类型入口文件路径
   */
  typeEntryFilePath: string;
}

/** 模板没有单独的type目录 */
export interface ResolvedPathsNoType extends ResolvedPathsBase {
  useTypeDir: false;
}

/** 解析后的路径信息 */
export type ResolvedPaths = ResolvedPathsHaveType | ResolvedPathsNoType;

/**
 * 解析模板
 */
export const resolveTemplate = (
  config: ConfigInfo,
  params: {
    series: string;
    name: string;
    full: string;
    cls: string;
    dir: string;
    rootPath: string;
  },
  list: ConfigInfoKey[],
): ResolvedPaths => {
  const data = {
    ...params,
    src: config.src,
    srcExample: config.srcExample,
    nameLowerFirst: _lowerFirst(params.name),
    $: "$",
  };

  const useTypeDir = !!config.type;

  const obj = list.reduce(
    (res, key) => {
      if (!useTypeDir) {
        if (key.startsWith("type.")) {
          console.log(
            chalk.green(
              `未配置type, ${key}路径解析跳过${Array(50 - key.length)
                .fill("")
                .join("_")}`,
            ),
          );
          return res;
        }
      }
      let templateConfig: string = _get(config, key, "");

      if (key.endsWith(".templateFilePath")) {
        const fileTemplateConfig = join(
          rootPath,
          configRootPath,
          templateConfig,
        );

        templateConfig = readFileSync(fileTemplateConfig, "utf-8");
      }

      const template = _template(templateConfig);

      res[key] = template(data);

      console.log(
        chalk.green(
          `${key}模板解析完成${Array(50 - key.length)
            .fill("")
            .join("_")}`,
        ),
      );

      return res;
    },
    {} as unknown as {
      [key in ConfigInfoKey]: string;
    },
  );

  const baseInfo: ResolvedPathsBase = {
    componentDirPath: join(rootPath, obj["component.dir"]),
    componentFileContent: obj["component.templateFilePath"],
    componentFilePath: join(
      rootPath,
      obj["component.dir"],
      obj["component.fileName"],
    ),

    componentEntryFileAddContent: obj["component.entryTemplate"],
    componentEntryFilePath: join(rootPath, obj["component.entryPath"]),

    styleDirPath: join(rootPath, obj["style.dir"]),
    styleFileContent: obj["style.templateFilePath"],
    styleFilePath: join(rootPath, obj["style.dir"], obj["style.fileName"]),

    styleVarFileAddContent: obj["style.varTemplate"],
    styleVarFilePath: join(rootPath, obj["style.varPath"]),

    styleEntryFileAddContent: obj["style.entryTemplate"],
    styleEntryFilePath: join(rootPath, obj["style.entryPath"]),

    exampleDirPath: join(rootPath, obj["example.dir"]),
    exampleFileContent: obj["example.templateFilePath"],
    exampleFilePath: join(
      rootPath,
      obj["example.dir"],
      obj["example.fileName"],
    ),

    exampleEntryFileAddContent: obj["example.entryTemplate"],
    exampleEntryFilePath: join(rootPath, obj["example.entryPath"]),
  };

  if (useTypeDir) {
    return {
      ...baseInfo,
      useTypeDir: true,
      typeDirPath: join(rootPath, obj["type.dir"]),
      typeFileContent: obj["type.templateFilePath"],
      typeFilePath: join(rootPath, obj["type.dir"], obj["type.fileName"]),

      typeEntryFileAddContent: obj["type.entryTemplate"],
      typeEntryFilePath: join(rootPath, obj["type.entryPath"]),
    };
  } else {
    return {
      ...baseInfo,
      useTypeDir: false,
    };
  }
};
